import { DataTypeAbstract, DataTypes, ModelAttributeColumnOptions, StringDataType } from "sequelize";
export enum EnumChangeType {
    none,
    numToStr,
    json,
    time,
}

export class ModelConfigBase {
    static readonly TABLE_NAME: string;
    protected mName: string;                        //报名
    protected mAttr: any;                           //字段
    protected mNeedSync: boolean = true;           //同步
    protected mForce: boolean = false;              //是否强制重建表格
    protected mAlter: boolean = false;                 //是否更新表字段
    protected mHooks: any;                          //hook观察者
    private _indexs: any[];

    constructor(name: string) {
        this.mAttr = {};
        this.mHooks = {};
        this._indexs = [];
        this.mName = name;
    }

    public init() {
        this.mInit();
    }

    protected mInit() {

    }

    protected mAddAttr(name: string, val: string | StringDataType | DataTypeAbstract | ModelAttributeColumnOptions, changeType: EnumChangeType = EnumChangeType.none) {
        if (val == DataTypes.DATE) {
            changeType = EnumChangeType.time;
        }
        if (!val['type']) {
            var newVal: any = { type: val };
        } else {
            var newVal: any = val;
        }
        if (changeType != EnumChangeType.none) {
            // this._changeType.push({ key: name, type: changeType });
            newVal.get = this.getChangeFunc(name, changeType);
            newVal.set = this.setChangeFunc(name, changeType);
        }
        this.mAttr[name] = newVal;
    }

    protected mAddIndexes(name: string, fields: string[], type: string = "Normal", method: string = "BTREE") {
        if (type == "Normal") {
            this._indexs.push({ name: this.name + "_" + name, method: method, fields: fields });
        } else {
            this._indexs.push({ name: this.name + "_" + name, method: method, type: type, fields: fields });
        }
    }

    protected mAddHook<T>(type: string, fn: (res: T) => void) {
        this.mHooks[type] = fn;
    }

    public getChangeFunc(key: string, changeType: EnumChangeType): () => any {
        if (changeType == EnumChangeType.numToStr) {
            return function () {
                return Number(this.getDataValue(key));
            }
        } else if (changeType == EnumChangeType.json) {
            return function () {
                var value = this.getDataValue(key);
                try {
                    return value ? JSON.parse(value) : null;
                } catch{
                    // logUtil.error("json转换出错" + value);
                    return null;
                }
            }
        } else if (changeType == EnumChangeType.time) {
            return function () {
                var value = this.getDataValue(key);
                return value ? value.getTime() : null;
            }
        }
    }

    public setChangeFunc(key: string, changeType: EnumChangeType): (val: any) => void {
        if (changeType == EnumChangeType.numToStr) {
            return function (value) {
                this.setDataValue(key, value.toString());
            }
        } else if (changeType == EnumChangeType.json) {
            return function (value) {
                try {
                    if (typeof value != "string") {
                        value = value ? JSON.stringify(value) : "";
                    }
                } catch (e) {
                    // logUtil.error("json转换出错" + value.toString());
                }
                this.setDataValue(key, value);
            }
        }
    }

    public setNumToStr(key: string): Function {
        return function (value: any) {
            this.setDataValue(key, value.toString());
        }
    }

    public get name(): string {
        return this.mName;
    }

    public get attr(): any {
        var attrs: any = {};
        for (let key in this.mAttr) {
            let value = this.mAttr[key];
            if (typeof value === 'object' && value['type']) {
                // value.allowNull = value.allowNull || false;
                attrs[key] = value;
            } else {
                attrs[key] = {
                    type: value,
                    // allowNull: false
                };
            }
        }
        return attrs;
    }

    public get needSync(): boolean {
        return this.mNeedSync;
    }

    public get force(): boolean {
        return this.mForce;
    }

    public get alter(): boolean {
        return this.mAlter;
    }

    public get hooks(): any {
        return this.mHooks;
    }

    public get indexes(): any[] {
        return this._indexs;
    }


    public get options(): any {
        var options = {
            tableName: this.name,
            modelName: this.name,
            timestamps: true,
            indexes: this.indexes,
            underscored: true,
            hooks: {}
        };
        for (var key in this.hooks) {
            options.hooks[key] = this.hooks[key];
        }
        return options;
    }
}